﻿package com.zouloux.physics 
{
	import com.zouloux.core.base.DocumentBase;
	import com.zouloux.core.debug.Debug;
	import flash.display.DisplayObject;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.TimerEvent;
	import flash.geom.Rectangle;
	import flash.utils.Timer;
	
	/**
	 * ...
	 * @author ZoulouX
	 */
	public class PhysicsEngine extends Sprite
	{
		// La gravité dans l'environnement
		protected var _gravity				:Number 			= 9;
		
		// Les limites de l'environnement
		protected var _limits				:Rectangle 			= new Rectangle(Number.MIN_VALUE, Number.MIN_VALUE, Number.MAX_VALUE, Number.MAX_VALUE);
		
		// Le timer de rafraichissement
		protected var _timer				:Timer				= new Timer(1);
		
		
		// Les getters
		public function get updateRate():uint
		{
			return 1000 / _timer.delay;
		}
		public function get gravity():Number
		{
			return _gravity;
		}
		public function get limits():Rectangle
		{
			return _limits;
		}
		public function get timer():Timer
		{
			return _timer;
		}
		
		// Les setters
		public function set updateRate(value:uint):void 
		{
			_timer.delay = 1000 / value;
		}
		public function set gravity(value:Number):void 
		{
			_gravity = value;
		}
		public function set limits(value:Rectangle):void 
		{
			_limits = value;
		}
		public function set timer(value:Timer):void
		{
			_timer = value;
		}
		
		
		// Création d'un nouvel environnement physique
		public function PhysicsEngine ()
		{
			Debug.add("com.zouloux.physics.PhysicsEngine{}");
			
			// Paramètrer le timer à la vitesse du stage
			_timer.delay = 1000 / DocumentBase.stage.frameRate;
			
			// Ecouter les tics du timer
			_timer.addEventListener(TimerEvent.TIMER, update);
		}
		
		// Ajouter un objet physique
		override public function addChild (pDisplayObject:DisplayObject):DisplayObject
		{
			Debug.add("com.zouloux.physics.PhysicsEngine.addChild(" + pDisplayObject + ")");
			
			// Vérifier si c'est bien un objet physique
			if (pDisplayObject is IPhysicsObject)
			{
				// Lier le moteur et l'objet
				(pDisplayObject as APhysicsObject).engine = this;
				
				// On ajoute
				return super.addChild(pDisplayObject);
			}
			else
			{
				// Déclancher l'erreur
				//throw new Error("PhysicsEngine.addChild() must add an IPhysicsObject");
				
				// On retourne un truc quand même
				return pDisplayObject;
			}
		}
		
		// Effacer un objet physique
		override public function removeChild (pDisplayObject:DisplayObject):DisplayObject
		{
			Debug.add("com.zouloux.physics.PhysicsEngine.removeChild(" + pDisplayObject + ")");
			
			// Vérifier si c'est bien un objet physique
			if (pDisplayObject is IPhysicsObject)
			{
				// Lier le moteur et l'objet
				(pDisplayObject as APhysicsObject).engine = this;
				
				// On supprime
				return super.removeChild(pDisplayObject);
			}
			else
			{
				// Déclancher l'erreur
				throw new Error("PhysicsEngine.removeChild() must add an IPhysicsObject");
				
				// On retourne un truc quand même
				return pDisplayObject;
			}
		}
		
		// Démarrer le moteur
		public function start ():void
		{
			Debug.add("com.zouloux.physics.PhysicsEngine.start()");
			_timer.start();
		}
		
		// Arrêter le moteur
		public function stop ():void
		{
			Debug.add("com.zouloux.physics.PhysicsEngine.stop()");
			_timer.stop();
		}
		
		// Mettre à jours les positions des éléments physiques selon leurs propriétés
		public function update (event:Event = null):void
		{
			// Récupérer toutes les instances
			for (var i:uint = 0; i < numChildren; i++)
			{
				// Récupérer l'instance de A
				var physicalObjectA:IPhysicsObject = this.getChildAt(i) as IPhysicsObject;
				
				// Récupérer toutes les autres instances
				//for (var j:uint = i; j < numChildren; j++)
				for (var j:uint = 0; j < numChildren; j++)
				{
					// Vérifier qu'on ne teste pas un objet sur lui même
					if (i != j)
					{
						// Récupérer l'instance de B
						var physicalObjectB:IPhysicsObject = this.getChildAt(j) as IPhysicsObject;
						
						// Actualiser cet objet
						physicalObjectA.checkCollisionWidth(physicalObjectB);
					}
				}
				
				// On actualise l'objet
				physicalObjectA.update();
			}
		}
	}
}